package fr.miage.course7.app.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;

public class CalculDistance {

	ArrayList<Integer> classementParticipants;
	
    /**
     * Constructeur.
     */
    public CalculDistance() {
    	classementParticipants = new ArrayList<Integer>();
    }

    /**
     * Calcule la distance entre deux points de coordonnes (x1,y1)  et (x2,y2).
     * @param x1 la latitude du point 1
     * @param y1 la longitude du point 1
     * @param x2 la latitude du point 2
     * @param y2 la longitude du point 2
     * @return la distance (double) entre les deux points
     */
    public final double calculDistanceAB(final double x1, final double y1,
                                  final double x2, final double y2) {
        double a = Math.PI / 180;
        double lat1 = x1 * a;
        double lat2 = x2 * a;
        double lon1 = y1 * a;
        double lon2 = y2 * a;

        double t1 = Math.sin(lat1) * Math.sin(lat2);
        double t2 = Math.cos(lat1) * Math.cos(lat2);
        double t3 = Math.cos(lon1 - lon2);
        double t4 = t2 * t3;
        double t5 = t1 + t4;
        double radDist = Math.atan(-t5 / Math.sqrt(-t5 * t5 + 1))
                          + 2 * Math.atan(1);

        return (radDist * 3437.74677 * 1.1508) * 1.6093470878864446;
    }

    /**
     * Calcule la distance a l'arrivee (en passant par l'itineraire).
     * @param etapeSuivante l'etape suivante
     * @param position la position actuelle
     * @param listeEtapes la liste des etapes de la course
     * @return la distance (double) a l'arrivee
     */
    public final double calculDistanceArrivee(final int etapeSuivante,
                                        final Position position,
                                        final ArrayList<Position> listeEtapes) {
        double result;
        result = calculDistanceAB(position.getLat(), position.getLon(),
                                listeEtapes.get(etapeSuivante).getLat(),
                                listeEtapes.get(etapeSuivante).getLon());
        for (int i = etapeSuivante; i < listeEtapes.size() - 1; i++) {
            result += calculDistanceAB(listeEtapes.get(i).getLat(),
                                        listeEtapes.get(i).getLon(),
                                        listeEtapes.get(i + 1).getLat(),
                                        listeEtapes.get(i + 1).getLon());
        }
        return result;
    }

    /**
     * Retourne un TreeMap Distance / ID participant (donc ordonne).
     * @param participants la collection des participants
     * @param checkpoints la collection des etapes de la course
     * @return un TreeMap<Double, Integer> correspondant
     *         a une collection par ordre croissant
     * des distances liees a des IDs de participants
     */
    public final TreeMap<Double, SortedSet<Integer>> getDistancesParticipants(
                 final HashMap<Integer, Participant> participants,
                 final ArrayList<Position> checkpoints) {
    	
    	SortedSet<Integer> participantsExAequo;
    	double distance;
    	
    	Set<Integer> idParticipants = participants.keySet();
        Iterator<Integer> it = idParticipants.iterator();

        TreeMap<Double, SortedSet<Integer>> distances = new TreeMap<Double, SortedSet<Integer>>();
        while (it.hasNext()) {
            Integer idParticipant = it.next();
            distance = calculDistanceArrivee(
            			participants.get(idParticipant).getDerniereEtape() + 1,
            			participants.get(idParticipant).dernierePosition(),
            			checkpoints);
            if (!distances.containsKey(distance)){
            	participantsExAequo = new TreeSet<Integer>();
            	participantsExAequo.add(idParticipant);
            	distances.put(distance,participantsExAequo);
            }
            else {
            	participantsExAequo = distances.get(distance);
            	participantsExAequo.add(idParticipant);
            	distances.put(distance,participantsExAequo);
            }
        }
        return distances;
    }

    /**
     * Affiche le classement par ordre croissant des participants.
     * Affichage avec leur distance a l'arrivee
     * @param participants la collection des participants
     * @param distancesParticipants collection par ordre croissant
     * des distances liees a des IDs de participants
     */
    public final void afficherClassementParticipants(
                        final HashMap<Integer, Participant> participants,
                        final TreeMap<Double, SortedSet<Integer>> distancesParticipants) {
        Set<Double> idDistancesParticipants = distancesParticipants.keySet();
        SortedSet<Integer> participantsExAequo;
        Iterator<Integer> it2;
        Iterator<Double> it = idDistancesParticipants.iterator();
        int i = 1;
        int y;
        while (it.hasNext()) {
            Double distanceParticipant = it.next();
            participantsExAequo = distancesParticipants.get(distanceParticipant);
            it2 = participantsExAequo.iterator();
            while (it2.hasNext()){
            	y = i;
            	Integer idParticipant = it2.next();
            	System.out.println("Position " + y + " : "
                        + participants.get(idParticipant).getNomBateau()
                        +" de categorie "
                        + participants.get(idParticipant).getCategorie()
                        +" pilote par "
                        + participants.get(idParticipant).getNomSkipper()
                        + " a "
                        + distanceParticipant
                        + " kilometres de l'arrivee");
            	i++;
            }
        }
    }
    
    /**
     * affecte � classementParticipants le classement par ordre croissant des participants.
     * @param distancesParticipants collection par ordre croissant
     * des distances liees a des IDs de participants
     */
    public final void setClassementParticipants(final TreeMap<Double, SortedSet<Integer>> distancesParticipants) {
    	ArrayList<Integer> result = new ArrayList<Integer>();
        Set<Double> idDistancesParticipants = distancesParticipants.keySet();
        SortedSet<Integer> participantsExAequo;
        Iterator<Integer> it2;
        Iterator<Double> it = idDistancesParticipants.iterator();
        int i = 1;
        int y;
        while (it.hasNext()) {
            Double distanceParticipant = it.next();
            participantsExAequo = distancesParticipants.get(distanceParticipant);
            it2 = participantsExAequo.iterator();
            while (it2.hasNext()){
            	y = i;
            	Integer idParticipant = it2.next();
            	result.add(idParticipant);
            }
        }
        classementParticipants = result;
    }
    
    /**
    * retourne le classement des participants (leur ID)
    * @return le classement des participants
    */
    public final ArrayList<Integer> getClassementParticipants(){
    	return classementParticipants;
    }

}
